#!/usr/bin/env python2
# -*- coding: utf-8 -*-

from Ump import utils
from Ump.lib import jsonobject
from Ump.common import exception, log, config


from Ump.objs.db import utils as db_utils
from Ump.objs.db import models 

from Ump.websocket.send import send_message
from flask import session as flask_session

from Ump.lich.base import LichBase
from Ump.objs.session_wrapper import _sw, get_token_from_flask_session
from agentapi import agent as agent_api

LOG = log.get_log('Ump.objs.task.manager')

task_callback_dict = {}

class TaskManager(object):
    #TODO -- phase2 - some aync invoke doens't need to db and ui actions
    #TODO -- phase3 - implement group tasks

    def __init__(self):
        self.lich = LichBase()

    def _get_one(self, id_or_spec):
        task = _sw.get_one(models.Task, id_or_spec=id_or_spec)
        if not task:
            LOG.warn("task is not exist in db")
            raise exception.NotFound(id_or_spec=id_or_spec)
        return task

    def _update_task_value(self, taskuuid, rsp):
        task = self._get_one({'uuid': taskuuid})

        response = jsonobject.loads(rsp)
        values = {
            'status': 'success',
            'detail': rsp,
            'finished_at': db_utils.todaynow(),
            'is_finished':True,
        }
        if not response.success:
            values['status'] = 'fail'
            values['detail'] = response.error

        if task:
            task.update(values)
        return task


    def on_callback(self, taskuuid, rsp_body):
        response = jsonobject.loads(rsp_body)
        #Find uuid from task_callback_dict

        #TODO - Verify whether uuid in dict, if not log warning and return
        #TODO - Remove uuid from task_callback_dict
        callback_func = task_callback_dict.pop(taskuuid, None)

        #TODO - Verify response
        # 1. host

        #Update Task DB
        task = self._update_task_value(taskuuid, rsp_body)


        #TODO - If task not exists

        response.host = task.host
        response.target = task.target
        response.target_id = task.target_id
        response.name = task.name
        response.task_id = task.id
        response.user_id = task.user_id
        response.task_uuid = task.uuid


        #Invoke Calback
        if callback_func:
            utils.exception_pass(callback_func, response)

        #Send to WS Socket
        if task.is_show:
            utils.exception_pass(send_message, jsonobject.dumps(response))

    def sync_post(self, cmd, callback=None):
        # TODO - Verify cmd

        # TODO - Verify path must exsit
        path = agent_api.cmd_path_dict[cmd.name]
        # TODO - Handle http invoke error
        try:
            cmd = self._set_cmd_host(cmd)
        except Exception, e:
            response = jsonobject.loads(self._get_error_response(cmd, e))
            response.target_id = cmd.target_id
            if callback:
                callback(response)
            raise Exception(e)

        cmd = self._set_cmd_is_show(cmd)
        cmd = self._set_cmd_timeout(cmd)

        token = get_token_from_flask_session()
        user_id = None
        if token:
            user_id = token.get('user_id')

        try:
            # Parse response and insert DB

            response = utils._exec_http(cmd, host=cmd.host, url=path)
        except Exception, e:
            response = jsonobject.loads(self._get_error_response(cmd, e))
            response.target_id = cmd.target_id
            if callback:
                callback(response)
            raise Exception(e)

        if callback:
            response.target_id = cmd.target_id
            utils.exception_pass(callback, response)

        if not response.success:
            raise exception.VolumeError(response.error)
        return response


    def async_post(self, cmd, callback=None):
        #TODO - Verify cmd
        # 1. cmd must have name, host
           
        #Gent uuid

        task_uuid = utils.uuid4()

        #Invoke async call
        #TODO - Verify path must exsit
        path = agent_api.cmd_path_dict[cmd.name]
        

        #TODO - Handle http invoke error
        try:
            cmd = self._set_cmd_host(cmd)
        except Exception, e:
            response = self._get_error_response(cmd, e)
            callback(jsonobject.loads(response))
            raise Exception(e)

        cmd = self._set_cmd_is_show(cmd)
        cmd = self._set_cmd_timeout(cmd)

        token = get_token_from_flask_session()
        user_id = None
        if token:
            user_id = token.get('user_id')

        try:
            # Parse response and insert DB
            task_values = {
                'status': 'processing',
                'uuid': task_uuid,
                'name': cmd.name,
                'target': cmd.target,
                'host': cmd.host,
                'target_id': cmd.target_id,
                'is_show': cmd.is_show,
                'timeout': cmd.timeout,
                'user_id': user_id,
            }
            task = models.Task(task_values).save()

            global task_callback_dict
            if callback:
                task_callback_dict[task_uuid] = callback

            res = utils._exec_http_async(cmd, task_uuid, host=cmd.host, url=path, port=config.storagent_port)
            #res = utils._exec_http_async(cmd, task_uuid, host=cmd.host, url=path)
        except Exception, e:
            response = self._get_error_response(cmd, e)
            self._update_task_value(task_uuid, response)
            callback(jsonobject.loads(response))
            raise Exception(e)

        #Format Task response and return result

        return task

    def _set_cmd_timeout(self, cmd):
        if not hasattr(cmd, 'timeout'):
            cmd.timeout = 300 
        return cmd

    def _set_cmd_host(self, cmd):
        if not cmd.host:
            host = self.lich._select_http(cluster_id=1)
            cmd.host = host
        return cmd

    def _set_cmd_is_show(self, cmd):
        is_show = True
        if hasattr(cmd, 'is_show') and cmd.is_show == False :
            is_show = False

        cmd.is_show = is_show
        return cmd

    def _get_error_response(self, cmd, e):
        rsp = agent_api.AgentResponse()
        rsp.success = False
        rsp.error = str(e)
        rsp.target = cmd.target
        rsp.host = cmd.host
        rsp.target_id = cmd.target_id
        return jsonobject.dumps(rsp)
        
    def _timeout_response(self, task, error):
        cmd = agent_api.AgentCommand()
        cmd.target = task.target
        cmd.host = task.host
        cmd.target_id = task.target_id
        return self._get_error_response(cmd, error)

    def delete_older(self):
        #models.Alert.query.fileter_by(deleted=1).all()
        models.Task.is_check_deleted = 1
        tasks = models.Task.query.all()
        models.Task.is_check_deleted = 0
        for task in tasks:
            if utils.time_greater_than(task.created_at, days=7) \
                    or ((not task.is_show) and utils.time_greater_than(task.created_at, hours=12)):

                if not task.deleted :
                    task.delete()
                task.hard_delete()
